|
Technote 1189The Monster Disk Driver Technote |
CONTENTSIntroduction |
This Technote is both a summary and review of existing disk driver information and a description of disk driver features that until now have not been generally documented. This Note is directed at developers of disk drivers and disk formatting utilities. There is also a section specifically aimed at application developers who need to operate on disks directly. |
IntroductionThe Mac OS disk driver architecture has not been comprehensively documented since Inside Macintosh II (1985). In the intervening years, disk technology has changed radically, from 400 KB floppy disks to FireWire, visiting two different SCSI Managers and four versions of ATA Manager on the way. Many of these technological changes have been accompanied by architectural changes for which the documentation is in obscure places, was not generally released, or was just never written. The technote is an attempt to rectify that oversight. It serves both to bring together the existing documentation and to fill in the missing pieces. You can use this technote as either a reference, an introduction to writing disk drivers, or just to bring yourself up-to-date on the latest disk driver advances. If you are new to Mac OS disk drivers, you should start with the Disk Driver Basics section. If you're already familiar with the basics of the Mac OS disk driver architecture, you may want to start with the two high-level summaries, one for disk driver writers and one for application developers. Existing InformationThe existing documentation for disk drivers is scattered through many different Apple documents, interface files, and code samples. The section classifies these references based on their usefulness. Core ReferencesThese large works cover information that you will definitely need in your driver. Don't start a disk driver without being familiar with these works:
Additional InformationThese smaller documents contain information that supplements the above in certain key areas.
ObsoleteThese documents, as they pertain to disk drivers, are considered obsolete. This list is provided for completeness only. You should read the recommended material instead.
Checklist for Disk Driver WritersAll of the above is probably overwhelming, so here is a summary of the most important steps to take to improve the reliability and compatibility of your disk driver:
For Application WritersThe purpose of a disk driver is to support a generic interface for accessing block devices. The primary client of this interface is the File Manager, although it can be used by other programs. If you're writing a foreign file system, or just an application that needs something beyond the standard File Manager programming interface, parts of this technote may be of interest to you.
In addition, if you're writing a disk formatting utility, this technote contain invaluable information on the partition map, chaining drivers, patch partitions, and "hostile" takeovers. |
Disk Driver BasicsMac OS communicates with attached devices through device drivers, which are software plug-ins that conform to a well-defined structure. The Device Manager is the original system component used to install, find, manage, and communicate with device drivers. It exports routines that can be called by higher level system software, and by applications. Most of these routines translate directly into requests to the underlying device driver. In order to identify different drivers, the Device Manager assigns each installed driver a unique negative number, referred to as a driver reference number. When calling the Device Manager, clients pass a driver reference number to tell it which driver they are dealing with. For a block device to be available to the system, it must have a disk driver. This is either in the ROM (for the built-in floppy drive), or loaded at system startup from a special partition on the disk (SCSI, ATA, and FireWire devices), or loaded from a system extension (USB and FireWire devices). In addition, a disk driver can be loaded when a device is plugged in by either an I/O family expert (ATA, USB, and FireWire), or by a special utility program (SCSI). Finally, software can install a disk driver for a virtual block device which has no obvious physical presence, such as a RAM disk or disk image. Regardless of how they are installed, all disk drivers roughly follow the same rules. It is important to note the difference between a disk and a device. A block device is the entity which reads and writes data on a disk. A disk is the medium which actually stores the data. This distinction is unimportant for fixed disk devices (such as hard disks), but is critical for removable disk devices (such as floppy drives and removable cartridge disk devices). Mac OS always directs block I/O to a software entity known as a drive. Each disk driver creates one or more drives and puts them in a system structure called the drive queue. Each drive queue element represents a drive, and contains both the driver reference number and the drive number. The drive number is a positive number that uniquely identifies the drive; it is assigned when the drive is added to the drive queue. A drive does not necessarily correspond directly to a given physical device. Rather, the driver decides which drives to create for the device it controls. In some cases, there is one drive per physical device. For example, the built-in floppy disk driver creates a drive for each attached floppy disk device. However, it is also common for a driver to create multiple drives for a single device. For example, the driver for a partitioned hard disk device creates a drive for each file system partition on the disk. When the system performs I/O to a drive, it supplies the driver reference number of the device driver and the drive number of a drive created by that driver. The Device Manager uses the driver reference number to find the device driver and call its entry point. The device driver then uses the drive number to determine which drive is the target of the I/O request. All drive I/O is done is terms of 512-byte logical blocks. Therefore, all transfers must start at multiple of 512 bytes and be a multiple of 512 bytes long. This is regardless of the underlying device's block size. File Manager and DrivesTo allow the flexibility of storage required by the user interface (a hierarchy of folders and files), Mac OS implements another layer of abstraction, known as the File Manager, on top of the Device Manager and the drive queue. A file system is a mechanism for storing fine-grained data (files) and meta-data (folders, Finders attributes, and so on) on a drive. The file system defines the way this data is stored and the rules for manipulating it. The File Manager includes built-in support for two file systems (HFS and HFS Plus) and a plug-in architecture (File System Manager) for others (AppleShare, DOS FAT, ProDOS , UDF, and third-party FSM plug-ins). The File Manager exports a programming interface defined in terms of volumes, which contain directories, files, and meta-data. A volume is an instance of a file system on a drive. Each volume is uniquely identified by a negative volume reference number, which is stored, along with other data to operate the volume, in a volume control block (VCB) that is linked into the system VCB queue. The VCB also contains the drive number and the driver reference number of the drive on which the volume is mounted. The process of making the contents of a drive available via the File Manager is called mounting a volume. When the File Manager attempts to mount a volume on a drive, it calls each of the file systems in turn to determine which one understands the logical format of the data on the disk in the drive. It then creates a VCB for that file system on that drive. The File Manager takes requests to operate on the volume and passes them to the appropriate plug-in file system, which reduces them to basic block operations and passes them to the drive via the Device Manager (using the drive number and driver reference number stored in the VCB). As far as the file system is concerned, the drive is its own logical disk, even though it may only represent a small part of the real disk. A drive can exist without having a volume mounted on it. This happens, for example, if the data format on the drive is incomprehensible to the installed file systems, or the volume on the drive has been unmounted. You can still access the data on a drive that has no volume mounted on it, but only via the Device Manager interface. TerminologyIn any technical document, it is very important to get your terminology straight. This is especially important when talking about disk drivers, where much of the terminology has been extended over the long, confusing history of the Mac OS block storage architecture. This technote uses the following terms throughout.
|
Driver GestaltAll disk drivers should support Driver Gestalt. Driver Gestalt is a mechanism whereby the system can query your driver to determine whether it supports advanced driver features. In many ways it is similar to the Mac OS Gestalt Manager, except that the system is querying your driver, not the other way around. Your driver should support Driver Gestalt. If you don't support Driver Gestalt, the system is in the dark as to which advanced driver features your driver supports. Driver Gestalt ReferenceThe basic reference for Driver Gestalt is Designing PCI Cards and Drivers for Power Macintosh Computers, specifically the "Driver Gestalt" section starting on page 106. However, Driver Gestalt is useful even on non-PCI computers. Your driver must support Driver Gestalt regardless of what computer or OS version it is running on. Designing PCI Cards and Drivers for Power Macintosh Computers does not document all of the selectors associated with Driver Gestalt. The only official, up-to-date list of Driver Gestalt selectors is the "DriverGestalt.h" header file, provided as part of Universal Interfaces. When Apple defines a new Driver Gestalt selector, we add the selector to "DriverGestalt.h", along with comments that describe how to implement it. In the event of a conflict between the written
documentation and "DriverGestalt.h", "DriverGestalt.h" is
correct and the written documentation is wrong. For example,
Designing
PCI Cards and Drivers for Power Macintosh Computers
describes the response of the Driver Gestalt GuaranteesBy saying that it supports Driver Gestalt, your driver guarantees certain things to the system, including:
Items 3 and 4 in the list above are not documented clearly in Designing PCI Cards and Drivers for Power Macintosh Computers, although they are implemented by all Apple drivers and are clearly shown in the various Driver Gestalt samples. This technote serves to officially document these two additional requirements. Driver Gestalt for ApplicationsProbably the best way to understand how to issue Driver Gestalt queries from an application is to look at some sample code. "Driver Gestalt Demo" is a simple sample that shows how to issue a few queries. "DriverGestaltExplorer" is a more comprehensive sample, which is also useful as a simple test and investigation tool. Both samples are available as DTS sample code. Summary of Driver GestaltAll disk drivers should support Driver Gestalt. |
Secrets of the Partition MapA number of features have been added to the Apple partition map since it was documented in Inside Macintosh: Devices. This section describes those features in detail. Partition Field RelevanceThe description of the
pmParType PossibilitiesInside
Macintosh: Devices documents the
well
known values for the
pmPartStatus RevealedInside
Macintosh: Devices says that the
The following flags are defined in
Bits 0 through 4 and 6 are still defined as documented in Inside Macintosh: Devices. A Mac OS formatting utility should always set these bit to 1 for file system partitions and clear them for other partition types. The second group of bits is used by Apple Mac OS disk drivers to hold information about file system partitions.
The third group of bits provides information about driver partitions. You may need to read Chaining Drivers and Patch Partitions to understand these descriptions.
Partition AttributesThere are a number of Control and Status requests that modify the attributes of a partition. A disk driver must support these requests as described below. A formatting application can use these requests to modify partition attributes.
Setting the Startup Partition
In response to this request, your disk driver must set
the partition described by
Determining Whether a Partition is the Startup Partition
In response to this request, your disk driver must set
The request returns the status that is currently recorded in the partition map, not whether the system actually started from this partition. Specifying That a Partition Should Be Mounted at Startup
In response to this request, your disk driver must set
the partition described by This request modifies the partition map, and hence only takes effect the next time the system is started. It does not affect the state of any volume currently mounted on the partition. Specifying That a Partition Should Not Be Mounted at Startup
In response to this request, your disk driver must set
the partition described by This request modifies the partition map and hence only takes effect the next time the system is started. It does not affect the state of any volume currently mounted on the partition. Determining Whether a Partition is to be Mounted
In response to this request, your disk driver must set
The request returns the status that is currently recorded in the partition map, not whether the partition was actually mounted at startup. Mounting a Partition Immediately
In response to this request, your disk driver must create
a drive queue element for the partition described by
If there is already a volume mounted on the partition, the system will ignore the "extra disk inserted" event this request generates. Locking a Partition
In response to this request, your disk driver must lock
the partition described by
Unlocking a Partition
In response to this request, your disk driver must unlock
the partition described by
Determining Whether a Partition is Locked
In response to this request, your disk driver must set
The request returns the status that is currently recorded
in the partition map, not whether the partition was actually
locked at startup. You can determine whether a drive is
currently write-protected by looking at bit 7 of the
pmPad PearlsA previously undocumented feature of the
Apple currently uses the following driver signatures:
The values have the following meaning:
Remember that your disk driver should use its own driver signature; do not use these values for your own driver. New Driver TypesInside
Macintosh: Devices describes how a Mac OS driver is
tagged by having
The following constants are defined for the
The driver type for a chained driver is always the two's complement of the driver type for the patch driver. For more information about this relationship, see Chaining Drivers and Patch Partitions. Driver ChecksumsInside Macintosh, Volume V (page 580)
contains an assembly language description of the checksum
algorithm used for the
The following is a C equivalent.
One minor mystery of the Inside Macintosh, Volume V also states that
driver checksumming is only done for if the first four bytes
of the driver's partition map entry The above algorithm is known as the 16-bit driver
checksum algorithm. This is because the ROM
decrements and tests
In some situations where the ROM loads a driver, it does not use the 16-bit checksum algorithm. Specifically, later versions of ATA Manager use a 32-bit driver checksum algorithm, shown below.
The key difference is that Your formatting utility must set Overall, the best solution to this driver checksum conundrum is:
A Partition of Your ImaginationThe original Mac Plus SCSI implementation did not allow the user to specify a startup partition. Obviously this is desired feature, and disk driver developers came up with a number of solutions for this problem. Over the years, Apple has introduced various stages of OS support for booting from a partition. Developer-Only SolutionsPrior to Apple providing a solution, developers were responsible for engineering their own. Developers quickly noticed that, all things being equal, the Macintosh tends to boot from the first bootable drive in the drive queue. Therefore, disk driver writers arranged to add the startup partition's drive queue element to the drive queue before the non-boot partitions' element. The disk driver's formatting utility provided the user interface for specifying the boot partition. This technique was relatively effective and stimulated user demand for a reliable mechanism for booting from a partition. Partition Attribute SupportEventually, Apple codified this approach and provided
support for it in the Startup Disk control panel. The
codification came in the form of the
This standardized the previous non-standard behavior, although it still is not a perfect solution because of variances in the way the ROM startup code chooses a drive from which to start up. SCSI Manager 4.3Apple made further refinements to this solution with the
introduction of SCSI Manager 4.3. SCSI Manager 4.3 presented
new problems to the startup code because it allows for
multiple SCSI buses, and it provides full support for SCSI
LUNs. So, when SCSI Manager 4.3
was introduced, Apple also
introduced a new technique for finding the startup
partition, the
When the user chooses a drive in the Startup Disk control
panel, Startup Disk sends the The
ROM-in-RAM (NewWorld)The ROM-in-RAM architecture, introduced with the iMac, presents new challenges for the startup device selection process. On a ROM-in-RAM machine, Open Firmware is responsible for loading the Mac OS ROM file off the startup partition, and hence Open Firmware must define the startup partition well before Mac OS starts to execute. When the Mac OS ROM starts, it continues booting from the startup partition chosen by Open Firmware to avoid the potential user confusion of loading the Mac OS ROM from one disk and the system software from another. Open Firmware synthesizes the traditional Macintosh startup process, including:
On ROM-in-RAM computers, the selected default startup
device is held in an Open Firmware configuration variable
It is impossible for Open
Firmware to completely mimic the startup drive selection
algorithm when it comes to selecting a startup partition.
When booting from a partition, Prior to Mac OS 9.0, the Startup Disk control panel used
tricky heuristics to allow booting from a partition with
Apple disk drivers as a temporary measure to solving this
problem. The long-term solution; however, is for disk
drivers to support a set of new Driver Gestalt queries,
which return exactly the information Startup Disk needs to
set |
Non-512 Byte Block DevicesThe original Mac OS disk driver architecture assumed that all block devices would use 512-byte blocks. Supporting block devices with a different block size is relatively simple, although it gets more complicated if you want to boot from such a device. Non-512 byte block device support is most important for CD-ROM drivers, which use a 2-KB block size. Just the BasicsThe basic rule for supporting non-512 block devices on Mac OS is that the disk driver is responsible for blocking and deblocking all I/O requests to a drive. This discussion assumes that the device block size is an integer multiple of 512, although similar algorithms work for weird device block sizes. Block TranslationThe File Manager makes an I/O request in terms of 512-byte logical block numbers on a particular drive. The disk driver is responsible for translating the logical block number of the request to an actual block number on the drive. If the disk is partitioned, the first step of this translation is to add the offset of the partition to the logical block number; this generates the physical block number. If the device uses 512-byte blocks, the physical block number is the actual block number of the data on the disk. If the device uses non-512 byte blocks, the disk driver must do a further translation, converting the physical block number to a device block number by dividing the physical block number by the number of 512-byte blocks in each physical block. In addition, the disk driver must block/deblock the request. If the physical block number, or the number of blocks to transfer, is not evenly divisible by the device block size, the disk driver must transfer partial blocks to and from the disk. The following diagrams shows the entire translation process for two partitions on a 2 KB block device. All numbers on the diagram are in the units labeled in the left column. For example, partition 1 is a 50 MB partition which extends from 0 to 100 mega logical blocks (512-byte blocks), 40 to 140 mega physical blocks (also 512-byte blocks), and 10 to 35 mega device blocks (2 KB byte blocks). Implementation NotesA disk driver typically deblocks a request by breaking it into three components. The leading component consists of all the requested physical blocks up to the first device block boundary. The leading component is empty if the requested physical blocks start on a device block boundary. The main component consists of all the requested physical blocks which are fully encompassed by device blocks. The main component may be empty if the transfer is short. The main component is transferred directly from between the client buffer and the disk. Finally, the trailing component consists of all the requested physical blocks of the transfer which fall after the last block of the main component. The trailing component is empty if the physical block number plus the number of physical blocks to transfer falls on a device block boundary. Because you can't transfer a sub-block size request, the leading and trailing components must be transferred through a temporary buffer. You should allocate this temporary buffer when your driver is opened. As the leading and trailing components are always less than one device block (otherwise they would be part of the main component), the temporary buffer need only be as big as a device block. If your device driver is single threaded, you need only allocate a single temporary buffer. If your driver is multi-threaded, you must allocate as many temporary buffers as you allow threads of execution within your driver, or internally serialize the use of the temporary buffer. The leading and trailing components are read by transferring the device block to the temporary buffer and then copying the appropriate data out of the temporary buffer to the client buffer. The leading and trailing components are written by first reading the current contents of the device to the temporary buffer, then copying the new data from the client buffer to the temporary buffer, then writing the temporary buffer to the device. The following illustration shows how misaligned read is transferred to the client buffer: Performance ConsiderationsThe above algorithm is obviously inefficient if transfers are misaligned, that is, if the leading and trailing components are not empty. Misaligned writes are even more expensive than misaligned reads because the disk driver must do an extra I/O to pre-fill the temporary buffer with the existing contents of device block. Worse yet, a misaligned write that has both leading and trailing components takes five I/O operations (read leading, write leading, write main, read leading, write leading). There are a number of ways to avoid misaligned transfers:
It is strongly recommended that your disk driver cache at least one device block. Many Mac OS programs will transfer data in sequential 512-byte chunks. By caching a single device block, your driver can radically reduce the average time taken to service these requests. Booting From Non-512 Byte Block DevicesThis section is not yet finished and has been omitted in the interests of shipping an initial version of the technote. A future revision of this technote will cover booting from a non-512 byte block device. If you are interested in this topic, please email DTS and ask for a prerelease draft of this section. |
Large Volume SupportWhen Mac OS originally shipped, it supported volume sizes up to 2 GB. This limit was shared by a number of system components, including the File Manager and disk drivers. Large volume support was introduced in two phases.
The changes to the File Manager programming interfaces are not relevant to this technote; they are documented in DTS Q&A FL 08, "Determining Volume Size." This section describes the changes to the disk driver interface. Large Volume InterfacesSupporting volumes between 2 GB and 4 GB was simply a
matter of redefining the
To support volumes larger than 4 GB, a new extended I/O
parameter block ( The C definition of the extended I/O parameter block is
given below. The key difference is the addition of the
For software making extended I/O requests, the fields are defined as follows:
For disk drivers servicing an extended I/O request, the fields are defined as follows:
Supporting Large Volumes in Your DriverTo support large volumes correctly, your driver must implement the following:
There are some important caveats of which you should be aware.
Notes for Developers Calling Disk DriversIf you're writing software that issues
The following code snippet implements these recommendations.
In addition, you should never call
|
How the ROM Loads SCSI and ATA DriversThis section describes how the ROM loads SCSI and ATA drivers from a driver partition. Understanding this process is critical to an understanding of the chaining driver architecture, and useful for general disk driver writers.
When a Macintosh boots, code in the ROM scans each SCSI and ATA bus for block devices in a bus-specific manner. Once it has found a potentially bootable block device, the ROM attempts to load a driver from that device. The ROM executes the following procedure to load a driver.
If any of these steps fail, the ROM assumes that the device is not bootable and attempts to boot from the next available device.
Each driver has two possible entry points. The primary entry point is at the beginning of the memory block holding the driver. The secondary entry point is 8 bytes into the memory block holding the driver. In general, the primary entry point is called when an "old" driver is loaded, or a "new" driver is loaded by an 'old' ROM, and the secondary entry point is used when a 'new' ROM load a "new" driver. The secondary entry point has extra parameters that make sense in the 'new' ROM environment. The exact definition of "old" and "new" depends on the
bootable bus. For SCSI, a "new" ROM is one that contains SCSI Manager 4.3, and a
"new" driver is indicated by the bytes "43" in the two bytes
following the "Apple_Driver" in
Both entry points use register-based calling conventions. The register usage is shown in the table below:
Register D3Old Apple SCSI drivers require that register D3 be set to a non-zero value in order to boot correctly. This bug was fixed in September 1996 although, if you are writing a SCSI disk -mounting utility, you may still encounter these old drivers. Register D5The data in register D5 depends on both the bootable bus and the entry point called. The following table indicates the possible combinations.
The format of
The fields have the following meaning:
In some cases (such as the
entry point to a patch loaded
by the Apple patch
driver), the
Register D0The significance of register D0 on return from your driver's entry point varies depending on the manager that loaded your driver.
|
Loading FireWire DriversThis section is only available under non-disclosure agreement. Please contact DTS for details. |
Chaining Drivers and Patch PartitionsBooting a computer is always a tricky exercise. One of the perennial challenges is working around problems in the ROM that prevent the OS from booting far enough to load patches in the normal way. On pre-ROM-in-RAM Macintosh computers, this problem is solved by means of chaining drivers and patch partitions. Patches loaded in this way have been used to:
This section explains how chaining drivers and patch partitions are implemented, and how you can license chaining drivers and patches suitable for inclusion in your own disk formatting utility.
Advice for Formatting UtilitiesThe first thing that a formatting utility must do is decide how many driver chains need to be constructed. This is determined by the number of possible bootable buses for the disk. For example, a SCSI device can only be attached via SCSI, so the utility need only construct one driver chain. In contrast, an removable cartridge disk might be placed in either a SCSI or ATA mechanism, and therefore must contain two driver chains, one for SCSI and one for ATA. Moreover, a PowerBook internal ATA hard disk device needs to have a SCSI driver chain if it is to work in target mode. For each driver chain constructed, the formatting utility
must first create a partition for the patch driver and then
create a partition for the disk driver itself. When creating
partitions, the formatting utility must be careful to write
the driver signature into the
In addition to creating the driver partitions, the formatting utility must also create entries in the DDM with the appropriate driver type. See New Driver Types for a list of driver types, and Architecture in Detail for an explanation of the relationship between them. The formatting utility must also construct the "Apple_Patches" partition. Some rules must be observed when doing this.
There are also some non-obvious factors when deciding whether to install a particular patch on a particular disk.
Finally, formatting utilities should aim to leave some free space in the partition maps, driver partitions, and patch partitions. Drivers and patches grow over time and wasting a few KB now may radically ease the job of upgrading a driver or patch in the future.
Architecture in DetailThis section describes the chaining driver architecture in detail, including how chaining drivers intercept the driver loading process, the Apple patch driver, and the structure of the patches it loads. To understand this section, you need to understand how the ROM loads SCSI and ATA drivers. Pre-Chaining ExampleThe following diagram shows how a partition map might be laid out prior to the introduction of chaining drivers. This example includes both ATA and SCSI drivers, a setup which is useful for disks that can be mounted in both ATA and SCSI mechanisms. Some salient features are:
Chaining DriversThe basic idea behind chaining drivers is very simple. A
chaining driver appears to the ROM as the actual disk
driver. It has a DDM entry of
the appropriate type ( The first chaining driver in a driver chain always has
the There are a number of important implementation details for chaining drivers.
How the
chaining driver handles errors depends on whether the
chaining driver precedes the disk driver in the driver
chain. If the chaining driver precedes the disk driver, any
error loading the next driver, or any error returned by the
next driver's entry point, is fatal. The chaining driver
should return The following diagram shows how a partition map might be laid out for a disk that can only be booted on an ATA bus and which has a chaining driver. Some salient features are:
The Apple Patch DriverThe Apple patch driver is a chaining driver supplied by Apple that loads patches from a special partition on the disk. You must license the patch driver and its accompanying patches for inclusion with your disk driver software. This section describes the operation of the patch driver insofar as is necessary for you to write a formatting utility that correctly installs the patches. Typically, the patch driver is installed first in the driver chain. It finds the patch partition by searching the partition map for an entry whose type is "Apple_Patches". It then walks the patch partition, loading and executing the patches. Finally, it chains to the next driver. The patch partition is structured to contain multiple
patches. The first block of the patch partition contains a
patch list, a description of all the
patches in the partition. The patch list is defined by the
The fields have the following meaning:
Each patch in the patch list is described by the
The fields have the following meaning:
When the patch driver executes a patch, it does so by creating a new pointer block in the system heap which is large enough to hold the patch, reading the patch code into that block, and then calling the patch entry point (the first byte of the memory block) using the calling conventions described in the next section. As part of its operation, the patch driver increases the size of the system heap to accommodate the size of the patches loaded. Patch Driver Error HandlingError handling in the patch driver follows the general
outline for error
handling in chaining drivers. Specifically, an error is
classified as either fatal or non-fatal. For a fatal error,
the patch driver discards the current patch descriptor and
patch code (if any) and returns
For a non-fatal error, the patch driver simply discards the patch descriptor and patch code for the patch and continues trying to load the next patch (if any) or the next driver in the driver chain. Non-fatal errors include:
Patch ExecutionThe prototype for a patch's entry point is given below.
The parameters to the entry point are:
The patch's code is always loaded in the system heap. The patch's entry point is always called at system task time.
Because a patch's code is always loaded in a pointer
block in the system heap, it can reduce its size in memory
using clever code sorting and The following code snippet shows how this might be achieved in C.
Putting It All TogetherThe following diagram shows the layout of a disk that can be booted via SCSI and ATA.
|
Disk Drivers and the System HeapDisk drivers typically allocate their memory in the system heap. A disk driver must use one of three techniques to allocate system heap space, depending on the execution context. There are three relevant execution contexts for your driver:
The best way to detect whether system startup is complete
is to compare the first byte (the length) of the Pascal
string returned by There is no good way to distinguish between driver load time and system startup time. Your driver must remember internally whether it is executing as a result of its install routine being called. Driver Load TimeAt driver load time, a driver that needs to allocate a
large amount of memory must grow the system heap using
A simple example of calling
System StartupDisk drivers that load as part of the
After Startup TimeAfter the system has started up, a disk driver should
allocate its system heap memory using
|
PowerPC Native Disk DriversMany developers wish to implement their disk drivers in PowerPC native code. However, there is no well-defined architecture for native disk drivers. There are a number of consequences and drawbacks, which this section discusses in detail. The Need for SpeedMost drivers are I/O bound. They spend a small amount of time setting up an I/O request and a proportionally much larger amount of time waiting for the underlying hardware to complete that request. Such drivers receive very little benefit from executing as native code. Moreover, the benefit varies depending on the ratio of small I/O requests (which tend to be CPU bound) to large I/O requests (which tend to be I/O bound). On the other hand, some drivers are CPU bound. For example, a driver that encrypts data as it transfers it to the disk may spend a significant amount of time executing driver code. This may even be true for a complex, but still I/O focused driver, such as a RAID driver or a caching disk driver. These drivers may receive significant benefit from "going native." The only good way to tell whether your driver receives a benefit from conversion to native code, and that the benefit is enough to overcome the difficulties in doing so, is to actually profile the code. You may be able to do this quickly by profiling the driver code in an application framework before facing the challenges of creating a working native driver. Difficulties with Taking Your Driver NativeThe primary difficulty in creating a native disk driver is that there is no well-defined architecture for it. The PCI-native driver model has a number of drawbacks for disk driver developers.
Another possible approach is to implement a partially native driver, where code that you know to take a long time is implemented as native code. This makes a lot of sense in some cases, such as an encryption driver, where the lengthy code is easily isolated from the rest of the driver. It is also possible to implement a virtually fully native
driver without the PCI native driver module, using only a
tiny amount of 68K glue code to provide the driver header
and an interface to When taking a disk driver native, it is important to remember that the primary client of the disk driver is the File Manager, which is not native. While it is likely that a disk driver will incur Mixed Mode switches regardless of whether it is native or not (the SCSI Manager and ATA Manager are native), taking the driver native shifts the line where the switches occur, and may increase or decrease the number of switches depending on how your driver works. So, to guarantee an overall speed improvement, it is important that the native driver be significantly faster than the emulated one. Native Drivers and accRunBefore implementing a disk driver as a native driver, you
must read DTS Q&A DV 35,
"Native
Drivers ( The rest of this technote assumes that you are building a
68K driver, and thus you can set 68K drivers should continue to use RecommendationsDTS does not recommend that developers implement disk drivers in PowerPC native code unless there is clear evidence that doing so improves the performance significantly. Typically this is only for drivers that are CPU bound, such as encrypting drivers. A standard SCSI or ATA driver is I/O bound, and receives little benefit from running native. The easiest way to implement a PowerPC native driver is using the native driver model, introduced with the PCI-based Power Macintosh computers. However, this approach will not work on older Power Macintosh computers. Another recommended alternative is to implement a partially native driver, where core functionality (such as an encryption engine) is in native code. |
Installing and Removing Drivers and DrivesOver the course of the past 15 years, Mac OS has evolved from a relatively static environment -- a Mac with one or two floppy drives that needed to be connected at startup time -- to a highly dynamic system, where devices and disks come and go at runtime. The Mac OS disk driver architecture has, to a large extent, coped with this evolution, as long as driver writers play by the rules. This section explains these rules in detail. Installing and Removing DriversThere are a number of ways to install your disk driver.
To remove a disk driver from the unit table, you have a number of choices.
Code SharingCode sharing is a technique used by some third-party disk drivers to share the device driver code between multiple drivers in the unit table. Code sharing is a legal technique, although it is not implemented by Apple disk drivers and is not recommended by DTS. Before shipping a driver that uses code sharing, you need to understand the costs and benefits of the technique. How Code Sharing WorksThe basic algorithm for code sharing is as follows:
In addition, drivers that implement code sharing must reference count the code in order to support close and purge correctly. The Pros and Cons of Code SharingCode sharing has one big advantage: it reduces memory usage if two devices controlled by your driver are attached to the system. This may be especially significant for a complex device driver, such as a RAID driver. The disadvantages of code sharing include:
Managing Drive Queue ElementsThe BasicsThe drive queue and its associated drive queue elements are documented in Inside Macintosh: Files, page 2-85. However, that document does not describe how drive queue elements are created, installed, removed, and destroyed. Your disk driver must add a drive queue element for each file system partition on each disk it controls. The strategy you use for managing drive queue elements is largely up to you, within some basic constraints. Drive queue elements must be allocated in the system heap, primarily so that they persist throughout the life of the system but also, in the case of paging devices, so that they are held resident in memory. Typically, your driver is responsible for creating and disposing the drive queue elements under your control. One popular technique for managing drive queue elements
is to extend the Another important thing to remember about drive queue
elements is that the system requires that you implement
four flag bytes immediately
before the first field of the
When creating a drive queue element, you must first decide on the drive number for the new drive. The algorithm to find a free drive number is very simple: start with drive number 5 (or, by convention, 8 if you're a hard disk driver), check to see whether it is in use, and if so, increment the number and try again.
Once your driver has created a drive queue element, it
can put it in the drive queue with the system routine
Once your driver has created a drive queue element, it should inform the system of its existence, as described in Cooperating with File System Manager. Removing a Drive Queue ElementRemoving a drive queue element is somewhat more
convoluted than adding one. The basics are very simple. The
system doesn't define a
Drive Queue StrategiesWhile removing a drive queue element is relatively
simple, deciding on a strategy for when to remove the drive
queue element is not. The key is how you handle the
Real Block DeviceIf your disk driver controls some real piece of hardware (for example, a floppy drive, a SCSI ejectable disk device, a SCSI fixed disk device), you should not remove the drive queue element when the user ejects the disk. You should leave the drive queue element in the queue so that, when the user reinserts the disk, you can post a "disk inserted" event for it. This simplifies your life and ensures that your drive's drive number is relatively stable. This approach may seem a little strange for fixed disks,
but it works just fine. Fixed disks are typically not marked
as ejectable, so the user can not really eject a fixed disk;
they simply unmount the volume mounted on it. This is useful
for programs (for example, a disk recovery program) which
want to unmount a volume, perform some low-level activity on
the disk, and then remount the volume. To remount the
volume, the program can simply call
So leaving fixed disk drive queue elements in the drive queue is not only safe, it is also convenient.
Virtual Block DeviceIf you are writing a disk driver for some virtual block device (like a RAM disk, or a disk image, or a block-oriented network protocol), your job is more complex. In the simple case, if the disk is ejected when there is no volume mounted on it, you should remove the drive queue element, as explained in the previous section. However, if the disk is ejected while there is still a volume mounted on it, you must take special action to avoid the disk switch dialog asking the user to insert the virtual disk. [The "Please insert disk RAM Disk" disk switch dialog is particularly amusing or annoying depending on how much caffeine you've had that day.] There are two common ways to prevent this:
Hot SwappingThe Mac OS I/O subsystem is evolving towards more support for hot-swappable devices. Modern I/O buses, like USB and FireWire, fully support the addition and removal of devices while the system is running. Unfortunately, other parts of Mac OS are not as friendly to the hot swapping of devices. For disk devices, hot swapping is a relatively new idea, and Mac OS support for hot swappable disk devices is limited. While it is possible to add new drives on the fly, removing a drive while there is a volume mounted on it will cause the system to crash, with possible loss of user data. There are two basic strategies for handling a disk device being unplugged unexpectedly.
In some cases, your I/O family may provide support for
the hot unplugging of disk devices. For example, if your
device is connected via the media bay, the system will
automatically put up a "put it back" dialog for you, and if
your device is connected via FireWire, you can use the
In the absence of an I/O family-specific solution, the best compromise solution is to implement the following algorithm:
|
Close and PurgeFor maximum friendliness, your driver must support being closed. This section explains how to support the Close request properly in you disk driver and how a formatting utility might use this to allow a disk to be reformatted without rebooting. Supporting Close in Your DriverYour driver must support the Close request properly. This requirement was documented a long time ago and is as true today as it ever was. Your driver's Close entry point should attempt to undo all the things that its Open entry point did, including the tasks listed below.
|
extern pascal SInt16 MoreVolumeMountedOnDrive(SInt16 drive, Boolean ejectedIsMounted) { SInt16 result; VCBPtr thisVCB; result = 0; thisVCB = (VCBPtr) GetVCBQHdr()->qHead; while (thisVCB != nil && result == 0) { if (thisVCB->vcbDrvNum == drive || (ejectedIsMounted && thisVCB->vcbDrvNum == 0 && thisVCB->vcbDRefNum == drive ) ) { result = thisVCB->vcbVRefNum; } else { thisVCB = (VCBPtr) thisVCB->qLink; } } return result; } |
If it is absolutely impossible to complete any of these
steps, the driver should return In addition, your driver may choose to implement the
|
struct DriverGestaltPurgeResponse { UInt16 purgePermission; UInt16 purgeReserved; Ptr purgeDriverPointer; }; typedef struct DriverGestaltPurgeResponse DriverGestaltPurgeResponse; |
If your driver responds to this selector, it must fill out the fields of the response as follows:
The bits in the
Of the eight possible combinations of these three bits,
only three make any real sense. There are symbolic constants
for these three useful combinations
( |
Note: |
IMPORTANT: |
Most existing disk drivers perform their driver initialization code in their Install routine and do nothing in their Open entry point. A typical SCSI driver's initialization code is as follows. |
on install install driver into unit table scan partition map create a drive queue element for each partition 'open' driver by marking it open in the DCE end install on open return noErr end open |
The problem with this approach is that it does not allow clients to reopen the driver after closing it. A better approach is shown below. |
on install install driver into unit table rename driver to a unique name open driver using OpenDriver end install on open scan partition map create a drive queue element for each partition end open |
WARNING: |
Note: This is not true for native drivers, where opens
and closes are reference counted by the Device
Manager. For a native driver, a second call to
|
Note: |
There are circumstances under which software wants to remove the driver for a disk at runtime. For example, a formatting utility might want to reformat a disk which was previously controlled by another driver. If the driver controlling the disk is written by you, it is easy to coordinate this takeover. On the other hand, if the driver controlling the disk is unknown to you, taking over the disk is tricky to do safely. This process is known as a hostile takeover. |
Note: |
To initiate a hostile takeover of a device, you must take the following steps.
|
Note: |
|
Note: |
If a hostile takeover is not possible without restarting -- or the user declines your offer to attempt one -- you are forced to restart the computer to take over the disk. You can overwrite the DDM to eliminate all foreign drivers from the disk and then restart the computer. Because there are no drivers in the DDM, the disk will not be mounted and you will be free to use it as you wish. |
IMPORTANT: |
Note: |
File Exchange (né PC Exchange)Foreign file systems (such as File Exchange) require your disk driver to do extra work to support the mounting of non-HFS volumes. While this extra work is not hard, it has been poorly documented. This section explains the correct way to support foreign file systems in your disk driver. |
Note: |
Cooperating with File System ManagerThere are two steps you must take to fully support File System Manager in your disk driver. The first step is to support the File Exchange interface, which is described in the next section. The second step is related to the way your disk driver scans a bus and creates drive queue elements for devices on that bus. Your current algorithm might look something like that shown below. |
on scanForDevices
scan bus for devices
for each device found on the bus
if the disk contains an Apple partition map
for each partition on the disk
if
|
To cooperate with FSM, you should modify this algorithm to the one shown below.
on scanForDevices
clear InformFSM flag
scan bus for devices
for each device found on the bus
if the disk contains an Apple partition map
for each partition on the disk
if
|
IMPORTANT: |
The basic algorithm, as shown above, is surprisingly easy. However, complications arise if your disk driver might load before FSM. This can happen in the following circumstances:
|
Note:
|
If your disk driver loads before FSM, the above algorithm
has a number of problems. Firstly, The solution to this is to defer both activities until
FSM loads. If system startup completes without FSM loading,
you simply do not perform these steps. You can poll for both
of these events in your driver's
|
on scanForDevices
determine whether FSM is installed
clear InformFSM flag
scan bus for devices
for each device found on the bus
if the disk contains an Apple partition map
for each partition on the disk
if
|
Note: |
For more information about why this algorithm is necessary, see Partition Handling: Background and Rationale. Finally, if you mount a large number of disks simultaneously, you may run afoul of the system event queue's size limit. On current systems (Mac OS 9.0), the system event queue is limited to 48 events. If the system event queue is full and you post a "disk inserted" event, the event is ignored. There are two aspects to this problem:
There is a simple algorithm that handles both of these cases:
Implementing File Exchange SupportThis section describes how you should implement the File Exchange interface in your disk driver. |
Note: |
A disk driver that supports the following Control and
Status requests must implement the Partition Information RecordThe partition information record
(
|
Note: |
Note: |
Trap |
_Control
|
Mode | Synch, Async, Immediate |
csCode
|
SInt16
|
-> |
kGetADrive (51)
|
csParam[0..1]
|
DrvQElPtr *
| -> | On input, contains the address of a drive queue element pointer. The request creates a new drive queue element based on the supplied drive queue element and places a pointer to the new drive queue element in the supplied address. |
In response to this request, your disk driver must create a new drive queue element. The fields of the new drive queue element must be filled out as described below.
Your driver must return the new drive queue element in
the memory pointed by |
IMPORTANT: |
IMPORTANT: |
Trap |
|
Mode |
Synch, Async |
|
|
-> |
|
|
|
-> |
The drive queue element whose partition is to be changed |
|
|
-> |
The block number of the first block in the partition |
|
|
-> |
The size (in blocks) of the partition |
In response to this request, your disk driver must
retarget the specified drive queue element to represent the
given partition on the disk. After this request, the drive
queue element must represent a partition that starts at the
block specified by You must not post a "disk inserted" event for the drive, or send the
|
IMPORTANT: |
Trap |
|
Mode |
Synch, Async |
|
|
-> |
|
|
|
-> |
A pointer to a |
In response to this request, your disk driver must mark
the partition specified |
IMPORTANT: |
Note: |
Note: |
Trap |
|
Mode |
Synch, Async |
|
|
-> |
|
|
|
-> |
The drive number of the drive whose partition information is requested |
|
|
-> |
A pointer to a |
In response to this request, your disk driver must place
partition information about the specified drive in the
partition information
record pointed to by |
Note: This raises the question, how does the Disk Initialization Package know whether a drive is a partition on a disk. The algorithm used is shown below.
The gist of this algorithm is that, if your driver supports File Exchange requests, the drive's partition must start at the beginning of the disk for the Disk Initialization Package to allow a change of format. Alternatively, if your driver does not support File Exchange requests, it is considered to have partitions if its unit number falls in the range reserved for classic SCSI Manager drivers. If other demands on your driver prevent it from being reformatted by the above algorithm, you will probably need to include reformat support in your formatting utility. DTS has requested a better solution to this problem [Radar ID 2287925]. |
Trap |
|
Mode |
Synch, Async |
|
|
-> |
|
|
|
-> |
A pointer to a |
|
|
-> |
A pointer to an |
In response to this request, your disk driver must
determine whether the partition described by the
partition information
record pointed to by |
Note: |
WARNING: |
Note: |
The first step of using the File Exchange interface is to create a drive queue element that targets the section of the disk you wish to read or write. The following code snippet shows how this might be done. |
extern pascal OSErr MoreCreateNewDriveQueueElement(SInt16 driveToClone, UInt32 firstBlock, UInt32 sizeInBlocks, SInt16 *newDrive) // See comment in interface part. { OSErr err; CntrlParam pb; DrvQElPtr drvQEl; // First check that the driver supports the File Exchange // interface. err = noErr; if ( ! MoreDriveSupportFileExchange(driveToClone) ) { err = controlErr; } // Find the drive queue element associated with // driveToClone. This is an input parameter to // kGetADrive. if (err == noErr) { err = MoreUTFindDriveQ(driveToClone, &drvQEl); } // Make the kGetADrive request to the driver. Because // we pass a pointer to memory outside of the parameter // block (drvQEl) and the driver might be a paging device, // we must hold drvQEl (and make sure to unhold it later!). if (err == noErr) { err = SafeHoldMemory(&drvQEl, sizeof(drvQEl)); if (err == noErr) { pb.ioVRefNum = driveToClone; pb.ioCRefNum = MoreGetDriveRefNum(driveToClone); pb.csCode = kGetADrive; *((DrvQElPtr **) &pb.csParam[0]) = &drvQEl; err = PBControlSync((ParmBlkPtr) &pb); if (err == noErr) { *newDrive = drvQEl->dQDrive; } (void) SafeUnholdMemory(&drvQEl, sizeof(drvQEl)); } } // Now retarget the new drive to the partition on the // disk specified by firstBlock and sizeInBlocks. We do // this in the create call because some disk drivers // don't always inherit the partition information from // the drive that was cloned. if (err == noErr) { err = MoreSetDrivePartition(*newDrive, firstBlock, sizeInBlocks); } return err; } |
This routine works in two parts. First, it finds the drive
queue element associated with |
extern pascal OSErr MoreSetDrivePartition(SInt16 drive, UInt32 firstBlock, UInt32 sizeInBlocks) // See comment in interface part. { OSErr err; CntrlParam pb; DrvQElPtr drvQEl; // First check that the driver supports the File Exchange // interface. err = noErr; if ( ! MoreDriveSupportFileExchange(drive) ) { err = controlErr; } // Find the drive queue element associated with // drive. This is an input parameter to // kRegisterPartition. if (err == noErr) { err = MoreUTFindDriveQ(drive, &drvQEl); } // Make the kRegisterPartition Control request. We // don't need to hold any memory because all the // parameters to this Control request are entirely // contained within the parameter block. if (err == noErr) { pb.ioVRefNum = drive; pb.ioCRefNum = MoreGetDriveRefNum(drive); pb.csCode = kRegisterPartition; *((DrvQElPtr *) &pb.csParam[0]) = drvQEl; *((UInt32 *) &pb.csParam[2]) = firstBlock; *((UInt32 *) &pb.csParam[4]) = sizeInBlocks; err = PBControlSync((ParmBlkPtr) &pb); } return err; } |
Once you have a drive queue element that spans the blocks
you're interested in, you can read and write those blocks
using standard Device Manager routines, for example,
|
static OSErr ReadBlock(SInt16 drive, UInt32 blockNumber, void *blockBuffer) { OSErr err; IOParam pb; pb.ioVRefNum = drive; pb.ioRefNum = MoreGetDriveRefNum(drive); pb.ioBuffer = blockBuffer; pb.ioReqCount = 512; pb.ioPosMode = fsFromStart; pb.ioPosOffset = blockNumber * 512; err = PBReadSync( (ParmBlkPtr) &pb ); return err; } |
Partition Handling: Background and RationaleTo understand the current disk driver architecture, you really need to understand the history of how it evolved, starting with the floppy disk drives on the Mac 128. Mac 128 Disk DriverWhen the original Mac shipped all disks were floppy disks, which did not support partitions. The floppy disk driver would create a single drive queue element that represented the entire disk, and the File Manager used this drive as the entire volume. There was a one-to-one translation between logical blocks on the volume (blocks that the File Manager requests) and physical blocks on the disk. For example, on a floppy disk, if the File Manager requests block 64, the disk driver would simply return block 64. Disk insertion was handled with the following algorithm:
SCSI and PartitionsThe introduction of SCSI hard disk devices on the Mac
Plus made this situation more complex. Hard disk devices
support multiple partitions. The File Manager was not
changed to recognize these partitions, so the burden of
supporting partitions fell on the disk driver. When a disk
is partitioned, the disk driver must read the partition map
and creates a drive queue element for each HFS partition (a
partition whose Thus, each drive queue element on a partitioned disk contains an implicit translation from logical blocks to physical blocks. For example, if you have a partition that starts at block 1024 and continues for 4096 blocks, the driver creates a drive queue element for a drive whose size is 4096 blocks. When the system reads logical block 64 on that volume, the driver knows that it must translate that to physical block 1088 (that is, 1024 + 64) on the disk. The new disk insertion algorithm was:
This works just fine for disks with the Apple partition map and HFS partitions, where the driver recognizes both the partition map format and the "Apple_HFS" partition map entries, and creates the appropriate drive queue elements. However, it doesn't allow foreign disk formats to be handled correctly, in two important cases.
A foreign file system (such as a File System Manager plug-in) is responsible for controlling a volume mounted on a particular drive (represented by a drive queue element). If there is no drive queue element for a partition, there is no obvious way to create one. Similarly, if there is no driver for a particular disk (because the disk doesn't have an Apple partition map to load it from), there is no easy way for the foreign file system to read from or write to the disk. File System ManagerWhen File System Manager was introduced, it defined a new way for disk drivers to announce the arrival of new drive queue elements. This mechanism allows disk drivers to create drive queue elements for non-Apple partitions, free from the fear of the dreaded "This is not a Macintosh disk. Would you like to initialize it?" dialog. The new algorithm works as described below:
The effect of these changes is that disk drivers are now
free to create a drive queue element for any partition and
will not trigger the Disk Initialization Package as long as
they set the FSID of the drive to
File ExchangeThe final part of the solution for problem 1 is the File Exchange interface for disk drivers, as defined above. To mount non-HFS partitions in an Apple partition map, File Exchange (and by extension any FSM plug-in) uses this interface in the following way.
A similar technique can be used for non-Apple partition maps. File Exchange also includes a partial solution to problem 2 in that it contains a generic SCSI disk driver. At startup time, File Exchange scans the SCSI bus looking for devices that contain DOS partition maps. When it finds such a device, it loads its generic SCSI driver for the device. Obviously that driver supports the File Exchange interface, which File Exchange then uses (in a similar process to that described above) to read through the DOS partition map and create drives for all the mountable DOS partitions on the disk. This is only a partial solution because (a) it only supports SCSI and ATA devices (the system includes a generic ATA device driver), but not any other block devices, and (b) the mechanism for loading the generic SCSI driver is not documented to developers. However, as a disk driver writer, you can craft your driver to guarantee a total solution to problem 2, as described in Cooperating with File System Manager. |
Private Control and Status RequestsIf you define private Control and Status requests for communication with your device driver, you must follow certain rules to ensure their reliable operation. This section outlines these rules. Private csCode SelectionIf your driver claims to supports
Driver Gestalt, it must not use
any Private Means PrivateIf you implement a Control or Status request that is
private to your driver, you must issue it only to your
driver. Do not issue your private Control and Status
requests to other drivers, because the other driver might
use the private At a minimum, you must check the driver name before issuing a private Control or Status request. You may also want to perform other checks (such as verifying a signature in the driver header, or issuing a private Driver Gestalt) just to be sure. Synchronous != System Task TimeAs described in DTS Technote 1067, "Traditional Device Drivers: Sync or Swim," calling a device driver synchronously does not guarantee that the driver's entry point will run at system task time. If you are defining a Control or Status request for which your driver must do something that is not interrupt safe, you must define the request to be executed immediately. Private Requests and Virtual MemoryIf your driver supports virtual memory (you can use the
The Virtual Memory Manager holds the entire
The problem comes when you define a private Control or
Status request whose There are a number of ways to avoid this problem.
If you're making a queued Control or Status request to a device driver which supports paging and the parameter block contains pointers to other data structures, you should hold those data structures, just to be sure. For more background about how the Mac OS Virtual Memory Manager prevents fatal page faults, see DTS Technote 1094, "Virtual Memory Application Compatibility." |
Read-Verify ModeVery few disk driver writers support read-verify mode in their drivers, perhaps on the mistaken assumption that it is difficult to do. This may be because the historical definition of read-verify mode in the ".Sony" driver is tricky to implement for any DMA-based peripheral. This section explains the current definition of read-verify mode, the best way to support it in your driver, and the best way for application software to use it. Read-Verify Mode ExplainedRead-verify mode is engaged by setting
This was easy to implement in the classic ".Sony" driver because the driver polled all bytes in to and out of memory. So implementing read-verify mode was a simple as changing the original copy loop:
This form of read-verify mode is tricky to implement in modern disk drivers, which typically use a DMA engine to transfer the data. So the definition of read-verify mode has changed, as explained in the next section. Implementing Read-Verify Mode in Your DriverThe new definition of read-verify mode is simple to explain, and to implement in your driver. If your driver gets a read-verify request, it should treat it exactly like a read request except that it must disable all caches for the request. The data transferred into memory must have originated from the physical medium itself. This new definition of read-verify mode still allows applications to perform read-verify operations, as explained in the next section. Using Read-Verify Mode in an ApplicationIt is easy to write software that uses read-verify mode
in way that is compatible with both the old and new definitions. The
This works because:
|
Color IconsA classic problem with disk drivers is that the mechanism
for returning icons from a disk driver (Control requests Mac OS 8.5 and later allow disk drivers to return color
icons. This is done through two new Driver Gestalt
selectors, You can build an icon family in a number of ways.
|
static IconFamilyPtr GetRamDiskIconFamily(void) { OSErr err; OSErr junk; IconFamilyPtr result; IconSuiteRef iconSuite; IconFamilyHandle iconFamily; Size iconFamilySize; result = nil; iconSuite = nil; iconFamily = nil; err = GetIconSuite(&iconSuite, 128, kSelectorAllAvailableData); if (err == noErr) { err = IconSuiteToIconFamily(iconSuite, kSelectorAllAvailableData, &iconFamily); } if (err == noErr) { iconFamilySize = GetHandleSize( (Handle) iconFamily); result = (IconFamilyPtr) NewPtrSys(iconFamilySize); err = MemError(); if (err == noErr && result == nil) { err = memFullErr; } } if (err == noErr) { BlockMoveData(*iconFamily, result, iconFamilySize); } // Clean up. if (iconSuite != nil) { (void) DisposeIconSuite(iconSuite, false); } if (iconFamily != nil) { DisposeHandle( (Handle) iconFamily); } return result; } |
IMPORTANT: |
IMPORTANT: |
Disk Driver Power ManagementThis section is not yet finished and has been omitted in the interests of shipping an initial version of the technote. A future revision of this technote will cover disk driver power management. In the meantime, you can consult the following references:
|
Target ModeMost PowerBooks support target mode (commonly known as "SCSI disk mode"), in which the attachment of a special cable causes the PowerBook to make its internal hard disk device available as a SCSI target device. For PowerBooks that use internal SCSI hard disk devices, support for target mode requires no special work by the disk driver. The PowerBook simply stays off of the SCSI bus and the host computer has free access to the PowerBook's internal hard disk device. However, for PowerBooks that use an internal ATA hard disk device, the implementation of target mode is somewhat more complex, and requires explicit support by the ATA disk driver. When a PowerBook with an internal ATA hard disk device boots in target mode, the CPU runs special target mode software. This software loads the ATA driver for the internal hard disk device and then puts the built-in SCSI controller into target mode, listening for incoming SCSI requests. When such a request is made, the CPU services that request by interpreting the incoming SCSI command. If the command requires disk I/O, the CPU makes an appropriate I/O request to the ATA disk driver to satisfy that I/O. In order to support target mode, your ATA disk driver must support some additional Control and Status requests that allow the target mode software to do its job. These requests are described in the remainder of this section. Target Mode ChecklistIf your ATA disk driver is having trouble when used in target mode, check that you support the following items.
Required Control and Status RequestsYour ATA driver must support the Control and Status requests described in this section in order to work in target mode. Switching to Physical I/O Mode
In response to this request, your disk driver must change
how it does logical-to-physical block translation on the
drive specified by For more details on logical-to-physical block translation, see Block Translation. If Returning Disk Size
In response to this request, your disk driver must return the physical size (in 512-byte blocks) of the disk in the device.
If Optional Status RequestsYour ATA driver may support the following Status requests to improve the fidelity of SCSI target emulation. Returning Error Information
In response to this request, your disk driver must return the information described above about the last error that occurred on the drive. If Getting Information About the Drive
In response to this request, your disk driver must return
the information described above about the attached drive.
The target mode software uses this information to satisfy a
SCSI Inquiry (
If |
Summary
This technote is the summary! |
|
|